#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#define N 32
#define M 256

#define L 0x1//登录
#define C 0x2//聊天
#define Q 0x3//退出
typedef struct{
	int type;//消息类型
	char name[N];
	char text[M];//消息正文
}MSG;
#define LEN sizeof(MSG)

typedef struct node{   //网络信息节点
	struct sockaddr_in addr;
	struct node *next;
}linknode,*linklist;

#define err_log(log)\
	do{\
	perror(log);\
	exit(1);\
	}while(0)
typedef struct sockaddr SA;
linklist create_list(void);
void do_login(int sockfd,struct sockaddr_in clientaddr,linklist H,MSG *msg);
void do_chat(int sockfd,struct sockaddr_in clientaddr,linklist H,MSG *msg);
void do_quit(int sockfd,struct sockaddr_in clientaddr,linklist H,MSG *msg);
int main(int argc, const char *argv[])
{
	
	int sockfd;
	struct sockaddr_in serveraddr,clientaddr;
	socklen_t len=sizeof(SA);
	bzero(&serveraddr,len);
	MSG msg;
	pid_t pid;
	linklist H;
	if(argc!=3)
	{
		fprintf(stderr,"user:%s ip port",argv[0]);
		return -1;
    }

	if((sockfd=socket(AF_INET,SOCK_DGRAM,0))<0)//创建数据报套接字
	{
		err_log("fail to socket:");
	}
    //指定的网络信息
	serveraddr.sin_family=AF_INET;
	serveraddr.sin_port=htons(atoi(argv[2]));
	serveraddr.sin_addr.s_addr=inet_addr(argv[1]);

    if(bind(sockfd,(SA*)&serveraddr,len)<0)
	{
		err_log("fail to bind:");
	}

	pid=fork();
	if(pid<0)
	{
		err_log("fail to fork:");
	}
	else if(pid==0)//发送系统信息
	{
		   bzero(&msg,LEN);
		   strcpy(msg.name,"server");
		   msg.type=C;
		   while(1)
		   {
			   fgets(msg.text,M,stdin);
			   msg.text[strlen(msg.text)-1]='\0';
			   sendto(sockfd,&msg,LEN,0,(SA*)&serveraddr,len);
		   }
	}
	else //群发信息
	{
			while(1)
			{
				H=create_list();
				while(1)
				{
					recvfrom(sockfd,&msg,LEN,0,(SA*)&clientaddr,&len);
					switch(msg.type)
					{
						case L:
							do_login(sockfd,clientaddr,H,&msg);
							break;
						case C:
							do_chat(sockfd,clientaddr,H,&msg);
							break;
						case Q:
							do_quit(sockfd,clientaddr,H,&msg);
							break;
						default:
							puts("error!");
							break;
					}
				}
			}
	}



	return 0;
}
linklist create_list(void)//创建链表
{
	linklist H;
	if((H=(linklist)malloc(sizeof(linknode)))==NULL)
	{
		perror("fail to malloc:");
		return NULL;
	}
	H->next=NULL;
	return H;
}

void do_login(int sockfd,struct sockaddr_in clientaddr,linklist H,MSG *msg)//登录处理
{
	linklist p=H->next;
	linklist q;
	sprintf(msg->text,"%s login.....",msg->name);
	socklen_t len=sizeof(SA);
	 while(p)//群发登录信息
	 {
			sendto(sockfd,msg,LEN,0,(SA*)&p->addr,len);//发送
			p=p->next;
	 }
	 if((q=(linklist)malloc(sizeof(linknode)))==NULL)
	 {
		perror("fail to malloc:");
		return;
	 }
	 q->addr=clientaddr;
	 //头插
	q->next=H->next;
	H->next=q;

	puts(msg->text);
}

void do_chat(int sockfd,struct sockaddr_in clientaddr,linklist H,MSG *msg)//聊天处理
{
	linklist p=H->next;
	char buf[M]={0};
	sprintf(buf,"%s said:%s",msg->name,msg->text);
	strcpy(msg->text,buf);
	socklen_t len=sizeof(SA);
	while(p)
	{
		if(memcmp(&clientaddr,&p->addr,sizeof(clientaddr))!=0)//不给自己发
		{
				sendto(sockfd,msg,LEN,0,(SA*)&p->addr,len);
		}
		p=p->next;
	}
	puts(msg->text);
}

void do_quit(int sockfd,struct sockaddr_in clientaddr,linklist H,MSG *msg)//退出处理
{
	linklist p=H;
	linklist q;
	sprintf(msg->text,"%s offline..........",msg->name);
	socklen_t len=sizeof(SA);
	while(p->next)
	{
		 if(memcmp(&clientaddr,&p->next->addr,sizeof(clientaddr))==0)//删除节点操作
		 {
				q=p->next;
				p->next=q->next;
				free(q);
				q=NULL;
		 }
		 else
		 {
			 sendto(sockfd,msg,LEN,0,(SA*)&p->next->addr,len);
			 p=p->next;
		 }
	}
	puts(msg->text);
}
